package algorithm.design;

import java.util.*;

public class Template {
    /*635. 设计日志存储系统*/
    /*你要的应该是 LeetCode 635. 设计日志存储系统 的题目内容：
    **题目描述**：
    请你设计一个日志系统，可以流式接收日志以及进行检索。
    实现`LogSystem`类：
    - `LogSystem()`初始化日志系统对象。
    - `void put(int id, string timestamp)`按时间顺序向日志系统中存入一个日志，该日志在存入时会有一个唯一的整数标识符`id`和一个表示时间戳的字符串`timestamp`，时间戳的格式为`"yyyy:MM:dd:HH:mm:ss"`。
    - `List<Integer> retrieve(string start, string end, string granularity)`检索在给定时间区间`[start, end]`（两个时间点都包括在内）内的日志条目。检索结果应包含所有日志条目在给定时间区间内的标识符`id`，并且按照时间戳`timestamp`升序排列返回。
    * 检索的时间区间以粒度`granularity`指定，它可以是`"Year"`、`"Month"`、`"Day"`、`"Hour"`、`"Minute"`或`"Second"`之一，表示检索的精度。例如，如果`granularity = "Day"`，则检索出在指定时间区间内每一天的所有日志条目。
    **注意**：
    - 所有的时间戳都是唯一的。
    - 时间戳的格式为`"yyyy:MM:dd:HH:mm:ss"`，精确到秒。
    - `start`和`end`的格式与`timestamp`相同，保证`start <= end`。
    - `granularity`是`"Year"`、`"Month"`、`"Day"`、`"Hour"`、`"Minute"`或`"Second"`之一。
    - 最多调用`put`和`retrieve`方法`500`次。
    */
    /*示例：输入：
    ["LogSystem","put","put","put","retrieve","retrieve"]
    [[],[1,"2017:01:01:23:59:59"],[2,"2017:01:01:22:59:59"],[3,"2016:01:01:00:00:00"],["2016:01:01:01:01:01","2017:01:01:23:00:00","Year"],["2016:01:01:01:01:01","2017:01:01:23:00:00","Hour"]]
    输出：
    [null,null,null,null,[3,2,1],[2,1]]

    解释：
    LogSystem logSystem = new LogSystem();
    logSystem.put(1, "2017:01:01:23:59:59");
    logSystem.put(2, "2017:01:01:22:59:59");
    logSystem.put(3, "2016:01:01:00:00:00");

    // 返回 [3,2,1]，返回从 2016 年到 2017 年的所有日志，按时间戳升序排列。
    logSystem.retrieve("2016:01:01:01:01:01", "2017:01:01:23:00:00", "Year");

    // 返回 [2,1]，只返回在 2016:01:01:01:01:01 到 2017:01:01:23:00:00 范围内小时精度的日志，按时间戳升序排列。
    logSystem.retrieve("2016:01:01:01:01:01", "2017:01:01:23:00:00", "Hour");*/
    static class LogSystem {
        private TreeMap<String, Integer> logs;
        private String[] granularities = {"Year", "Month", "Day", "Hour", "Minute", "Second"};
        private int[] granularityIndexes = {4, 7, 10, 13, 16, 19};

        public LogSystem() {
            logs = new TreeMap<>();
        }

        public void put(int id, String timestamp) {
            logs.put(timestamp, id);
        }

        public List<Integer> retrieve(String start, String end, String granularity) {
            List<Integer> result = new ArrayList<>();
            int index = getGranularityIndex(granularity);
            String truncatedStart = "";
            String truncatedEnd = "";

            if (granularity.equals("Year")) {
                truncatedStart = start.substring(0, index) + ":01:01:00:00:00";
                truncatedEnd = end.substring(0, index) + ":12:31:23:59:59";
            } else if (granularity.equals("Month")) {
                truncatedStart = start.substring(0, index) + ":01:00:00:00";
                int year = Integer.parseInt(end.substring(0, 4));
                int month = Integer.parseInt(end.substring(5, 7));
                int lastDayOfMonth = getLastDayOfMonth(year, month);
                truncatedEnd = end.substring(0, index) + ":" + lastDayOfMonth + ":23:59:59";
            } else if (granularity.equals("Day")) {
                truncatedStart = start.substring(0, index) + "00:00:00";
                truncatedEnd = end.substring(0, index) + "23:59:59";
            } else if (granularity.equals("Hour")) {
                truncatedStart = start.substring(0, index) + ":00:00";
                truncatedEnd = end.substring(0, index) + ":59:59";
            } else if (granularity.equals("Minute")) {
                truncatedStart = start.substring(0, index) + ":00";
                truncatedEnd = end.substring(0, index) + ":59";
            } else if (granularity.equals("Second")) {
                truncatedStart = start;
                truncatedEnd = end;
            }

            for (Map.Entry<String, Integer> entry : logs.tailMap(truncatedStart).entrySet()) {
                if (entry.getKey().compareTo(truncatedEnd) > 0) {
                    break;
                }
                result.add(entry.getValue());
            }
            return result;
        }

        private int getGranularityIndex(String granularity) {
            for (int i = 0; i < granularities.length; i++) {
                if (granularities[i].equals(granularity)) {
                    return granularityIndexes[i];
                }
            }
            return -1;
        }

        private boolean isLastDayOfMonth(int year, int month, int day) {
            int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            if (month == 2 && isLeapYear(year)) {
                return day == 29;
            } else {
                return day == daysInMonth[month - 1];
            }
        }

        private int getLastDayOfMonth(int year, int month) {
            int day = 1;
            int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            if (month == 2 && isLeapYear(year)) {
                day = 29;
            } else {
                day = daysInMonth[month - 1];
            }
            return day;
        }

        private boolean isLeapYear(int year) {
            return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
        }
    }

    public static void main(String[] args) {
        LogSystem logSystem = new LogSystem();
        logSystem.put(1, "2017:01:01:23:59:59");
        logSystem.put(2, "2017:01:01:22:59:59");
        logSystem.put(3, "2016:01:01:00:00:00");
        // return [1,2,3], because you need to return all logs within 2016 and 2017.
        List<Integer> list1 = logSystem.retrieve("2016:01:01:01:01:01", "2017:01:01:23:00:00", "Year");
        // return [1,2], because you need to return all logs start from 2016:01:01:01 to 2017:01:01:23, where log 3 is left outside the range.
        List<Integer> list2 = logSystem.retrieve("2016:01:01:01:01:01", "2017:01:01:23:00:00", "Hour");
        // 月份记得考虑28天、29天、30天、31天的场景
        List<Integer> list3 = logSystem.retrieve("2016:01:01:01:01:01", "2017:02:01:23:00:00", "Month");
    }
}
